/* Sentinel - IRC Statistical and Operator Services
** htmlsup.c - Support functions to the HTML dump.
**
** Copyright W. Campbell and others.  See README for more details
** Some code Copyright: Jonathan George, Kai Seidler, ircd-hybrid Team,
**                      IRCnet IRCD developers.
**
** $Id: htmlsup.c 7 2010-02-01 19:17:42Z dubkat $
*/

#include "stats.h"
#include "struct.h"
#include "proto.h"
#include "lang.h"

#define SINGLE		0
#define START_NEEDED	1
#define END_NEEDED	2

/* Certain characters have to be escaped when put into an HTML file.
** These characters often are replaced by longer strings, so a simple
** character replacement will not work.
*/

struct StringReplace
{
  char character;
  int type;
  int len;
  char *replacement;
  int len2;
  char *repl2;
};

static struct StringReplace replace_list[] =
  {
    { '\002', START_NEEDED, 3, "<B>", 4, "</B>" },
    { '\037', START_NEEDED, 3, "<U>", 4, "</U>" },
    { '<', SINGLE, 4, "&lt;", 0, NULL },
    { '>', SINGLE, 4, "&gt;", 0, NULL },
    { '\"', SINGLE, 6, "&quot;", 0, NULL },
    { '&', SINGLE, 5, "&amp;", 0, NULL },
    { ' ', SINGLE, 0, NULL, 0, NULL }
  };

static struct StringReplace *replace_array[256];

void InitHTMLArray(void)
{
  int i;

  for (i = 0; i < 256; i++)
    replace_array[i] = NULL;

  for (i = 0; replace_list[i].replacement != NULL; i++)
    replace_array[(int) replace_list[i].character] =
      &replace_list[i];
}

char *HTMLReplace(char *source)
{
  int i;
  int j = 0;
  static char dest[BUFSIZE];

  if (source == NULL)
    return NULL;

  dest[0] = '\0';

  for (i = 0; source[i] != '\0'; i++)
  {
    if (j == BUFSIZE)
    {
      dest[BUFSIZE - 1] = '\0';
      return dest;
    }
    if (replace_array[(int) source[i]] != NULL)
    {
      if (replace_array[(int) source[i]]->type == SINGLE)
      {
        dest[j] = '\0';
        strlcat(dest,
                replace_array[(int) source[i]]->replacement,
                BUFSIZE);
        j += replace_array[(int) source[i]]->len;
      }
      else if (replace_array[(int) source[i]]->type ==
               START_NEEDED)
      {
        dest[j] = '\0';
        strlcat(dest,
                replace_array[(int) source[i]]->replacement,
                BUFSIZE);
        j += replace_array[(int) source[i]]->len;
        replace_array[(int) source[i]]->type = END_NEEDED;
      }
      else if (replace_array[(int) source[i]]->type ==
               END_NEEDED)
      {
        dest[j] = '\0';
        strlcat(dest,
                replace_array[(int) source[i]]->repl2,
                BUFSIZE);
        j += replace_array[(int) source[i]]->len2;
        replace_array[(int) source[i]]->type = START_NEEDED;
      }
    }
    else
    {
      dest[j] = source[i];
      j++;
    }
  }

  if (j == BUFSIZE)
    dest[BUFSIZE - 1] = '\0';
  else
    dest[j] = '\0';

  return dest;
}

/* This is a little inefficient, as it has to construct the sorted list
** each time the HTML dump is run.
*/
void PrintTopChannels(FILE *fp, int num)
{
  dlink_list sorted_chanlist;
  dlink_node *dl;
  dlink_node *dl2;
  struct Channel *c;
  struct Channel *c2;
  int i;

  sorted_chanlist.head = NULL;
  sorted_chanlist.tail = NULL;

  /* Construct the list out of the channel hash table */
  for (i = 0; i < C_TABLE_SIZE; i++)
  {
    for (c = chanlist[i]; c != NULL; c = c->next)
    {
      /* We're only displaying visible channels */
      if (IsVisibleChannel(c))
      {
        int broken = FALSE;

        /* Go through the list of already sorted channels, dlink_add_before()
        ** when we find the one we want.
        */
        DLINK_FOREACH(dl2, sorted_chanlist.head)
        {
          c2 = (struct Channel *) dl2->data;

          if (c->nummembers > c2->nummembers)
          {
            dl = dlink_create();
            dlink_add_before(dl2, c, dl, &sorted_chanlist);
            broken = TRUE;
            break;
          }
        }
        if (broken)
          continue;
        /* If we get here, then the original list was empty OR the usercount
        ** is smaller than any other channel already in the sorted list.  Add
        ** it to the tail of the list.
        */
        dl = dlink_create();
        dlink_add_tail(c, dl, &sorted_chanlist);
      }
    }
  }

  /* The list is created, now construct the HTML table */
  fprintf(fp, "<TABLE BORDER=\"1\"><TR><TH>%s</TH><TH>%s</TH><TH>%s</TH></TR>\r",
          H_RANK, H_CHANNAME, H_CAPUSERS);

  i = 0;
  DLINK_FOREACH(dl, sorted_chanlist.head)
  {
    c = (struct Channel *) dl->data;

    i++;
    fprintf(fp, "<TR><TD>%d</TD><TD>%s</TD><TD>%d</TD></TR>\r", i, c->name,
            c->nummembers);

    if (i >= num)
      break;
  }

  fprintf(fp, "</TABLE>\r");

  /* Now release all of the memory we're using */
  DLINK_FOREACH_SAFE(dl, dl2, sorted_chanlist.head)
  {
    dlink_delete(dl, &sorted_chanlist);
    dlink_free(dl);
  }
}
